	.code16
# rewrite with AT&T syntax by falcon <wuzhangjin@gmail.com> at 081012
#
# SYS_SIZE is the number of clicks (16 bytes) to be loaded.
# 0x3000 is 0x30000 bytes = 196kB, more than enough for current
# versions of linux
#
	.equ SYSSIZE, 0x3000
#
#	bootsect.s		(C) 1991 Linus Torvalds
#
# bootsect.s is loaded at 0x7c00 by the bios-startup routines, and moves
# iself out of the way to address 0x90000, and jumps there.
#
# It then loads 'setup' directly after itself (0x90200), and the system
# at 0x10000, using BIOS interrupts. 
#
# NOTE! currently system is at most 8*65536 bytes long. This should be no
# problem, even in the future. I want to keep it simple. This 512 kB
# kernel size should be enough, especially as this doesn't contain the
# buffer cache as in minix
#
# The loader has been made as simple as possible, and continuos
# read errors will result in a unbreakable loop. Reboot by hand. It
# loads pretty fast by getting whole sectors at a time whenever possible.

	.global _start, begtext, begdata, begbss, endtext, enddata, endbss
	.text
	begtext:
	.data
	begdata:
	.bss
	begbss:
	.text

	.equ SETUPLEN, 4		# nr of setup-sectors
	.equ BOOTSEG, 0x07c0		# original address of boot-sector
	.equ INITSEG, 0x9000		# we move boot here - out of the way  防止后续加载代码进行覆盖，因此将第一个磁盘的内容从0x7c00处复制到0x9000处，并将后续磁盘的内容依次复制到后面
	.equ SETUPSEG, 0x9020		# setup starts here
	.equ SYSSEG, 0x1000		# system loaded at 0x10000 (65536).
	.equ ENDSEG, SYSSEG + SYSSIZE	# where to stop loading

# ROOT_DEV:	0x000 - same type of floppy as boot.
#		0x301 - first partition on first drive etc
#
##和源码不同，源码中是0x306 第2块硬盘的第一个分区
#
	.equ ROOT_DEV, 0x301
	ljmp    $BOOTSEG, $_start
_start:
	mov	$BOOTSEG, %ax	#将ds段寄存器设置为0x7C0
	mov	%ax, %ds    #在8086 CPU架构的限制下，不能将立即数（直接给出的数值）写入段寄存器中（如ds，cs，es，ss等），因此就必须通过一个中转，这个中转就是ax寄存器。
	mov	$INITSEG, %ax	#将es段寄存器设置为0x9000
	mov	%ax, %es
	mov	$256, %cx		#设置移动计数值256字
	sub	%si, %si		#源地址	ds:si = 0x07C0:0x0000      subl %eax,%edx 表示将%edx-%eax的结果存放进寄存器edx中  --> sub	%si, %si --> %si为0
	sub	%di, %di		#目标地址 es:di = 0x9000:0x0000
	rep					#重复执行并递减cx的值
	movsw				#从内存[si]处移动cx个字到[di]处  将内存地址 0x7c00 处开始往后的 512 字节的数据，原封不动复制到 0x90000 处
	ljmp	$INITSEG, $go	#段间跳转，这里INITSEG指出跳转到的段地址，解释了cs的值为0x9000 --> 表示跳转到 0x9000:go 处执行

# ljmp	$INITSEG, $go --> cs 寄存器里的值就是 0x9000，ip 寄存器里的值是 go 这个标签的偏移地址。那这三个 mov 指令就分别给 ds、es 和 ss 寄存器赋值为了 0x9000。

go:	mov	%cs, %ax		#将ds，es，ss都设置成移动后代码所在的段处(0x9000)
	mov	%ax, %ds
	mov	%ax, %es
# put stack at 0x9ff00. ss 为栈段寄存器，栈基址寄存器 sp 来表示此时的栈顶地址。而此时 sp 寄存器被赋值为了 0xFF00 了，所以目前的栈顶地址就是 ss:sp 所指向的地址 0x9FF00 处。
	mov	%ax, %ss
	mov	$0xFF00, %sp		# arbitrary value >>512

# load the setup-sectors directly after the bootblock.
# Note that 'es' is already set up.

#
##ah=0x02 读磁盘扇区到内存	al＝需要独出的扇区数量
##ch=磁道(柱面)号的低八位   cl＝开始扇区(位0-5),磁道号高2位(位6－7)
##dh=磁头号					dl=驱动器号(硬盘则7要置位)
##es:bx ->指向数据缓冲区；如果出错则CF标志置位,ah中是出错码
#
load_setup:
	mov	$0x0000, %dx		# drive 0, head 0
	mov	$0x0002, %cx		# sector 2, track 0
	mov	$0x0200, %bx		# address = 512, in INITSEG --> es:bx = 0x9000:0x200
	.equ    AX, 0x0200+SETUPLEN
	mov     $AX, %ax		# service 2, nr of sectors  --> ah=0x02, al=0x04
	int	$0x13			# read it
	jnc	ok_load_setup		# ok - continue
	mov	$0x0000, %dx
	mov	$0x0000, %ax		# reset the diskette
	int	$0x13
	jmp	load_setup

ok_load_setup:

# Get disk drive parameters, specifically nr of sectors/track

	mov	$0x00, %dl
	mov	$0x0800, %ax		# AH=8 is get drive parameters
	int	$0x13
	mov	$0x00, %ch
	#seg cs
	mov	%cx, %cs:sectors+0	# %cs means sectors is in %cs
	mov	$INITSEG, %ax
	mov	%ax, %es

# Print some inane message

	mov	$0x03, %ah		# read cursor pos
	xor	%bh, %bh
	int	$0x10
	
	mov	$30, %cx
	mov	$0x0007, %bx		# page 0, attribute 7 (normal)
	#lea	msg1, %bp
	mov     $msg1, %bp
	mov	$0x1301, %ax		# write string, move cursor
	int	$0x10

# ok, we've written the message, now
# we want to load the system (at 0x10000)

	mov	$SYSSEG, %ax
	mov	%ax, %es		# segment of 0x010000
	call	read_it
	call	kill_motor

# After that we check which root-device to use. If the device is
# defined (#= 0), nothing is done and the given device is used.
# Otherwise, either /dev/PS0 (2,28) or /dev/at0 (2,8), depending
# on the number of sectors that the BIOS reports currently.

	#seg cs
	mov	%cs:root_dev+0, %ax
	cmp	$0, %ax
	jne	root_defined
	#seg cs
	mov	%cs:sectors+0, %bx
	mov	$0x0208, %ax		# /dev/ps0 - 1.2Mb
	cmp	$15, %bx
	je	root_defined
	mov	$0x021c, %ax		# /dev/PS0 - 1.44Mb
	cmp	$18, %bx
	je	root_defined
undef_root:
	jmp undef_root
root_defined:
	#seg cs
	mov	%ax, %cs:root_dev+0

# after that (everyting loaded), we jump to
# the setup-routine loaded directly after
# the bootblock:

	ljmp	$SETUPSEG, $0

# This routine loads the system at address 0x10000, making sure
# no 64kB boundaries are crossed. We try to load it as fast as
# possible, loading whole tracks whenever we can.
#
# in:	es - starting address segment (normally 0x1000)
#
sread:	.word 1+ SETUPLEN	# sectors read of current track
head:	.word 0			# current head
track:	.word 0			# current track

read_it:
	mov	%es, %ax    #将ES寄存器的值移动到AX寄存器
	test	$0x0fff, %ax    #测试AX的低12位是否为0（检查ES是否在64KB边界上） 实模式下，内存地址的访问通过Segment:Offset的方式来进行的，其中Segment是段的Base Address，一个Segment的最大长度是64 KB，这是16-bit系统所能表示的最大长度
die:	jne 	die			# es must be at 64kB boundary 如果不是，跳转到标签die，形成无限循环
	xor 	%bx, %bx		# bx is starting address within segment  将BX寄存器清零，用作段内起始地址
rp_read:
	mov 	%es, %ax    #再次将ES寄存器的值移动到AX寄存器
 	cmp 	$ENDSEG, %ax		# have we loaded all yet?    比较AX和ENDSEG，检查是否已经读取了所有数据
	jb	ok1_read    #如果AX小于ENDSEG，跳转到ok1_read
	ret    #如果已经读取完毕，返回
ok1_read:
	#seg cs    #将下一段代码的段寄存器设置为cs
	mov	%cs:sectors+0, %ax    #将sectors的值（在最后）移动到AX寄存器
	sub	sread, %ax    #从AX中减去sread的值，计算剩余需要读取的扇区数
	mov	%ax, %cx    #将计算结果移动到CX寄存器
	shl	$9, %cx    #将CX左移9位，转换为字节偏移量
	add	%bx, %cx    #将BX（段内起始地址）加到CX（偏移量）
	jnc 	ok2_read    #如果没有发生进位，跳转到ok2_read
	je 	ok2_read    #如果CX等于0xFFFF，也跳转到ok2_read
	xor 	%ax, %ax    #清零AX寄存器
	sub 	%bx, %ax    #计算BX的补码
	shr 	$9, %ax    #将AX右移9位，转换回扇区数
ok2_read:
	call 	read_track    #调用read_track函数读取磁盘扇区
	mov 	%ax, %cx    #将返回的扇区数移动到CX寄存器
	add 	sread, %ax    #将sread的值加到AX（已读取扇区数）
	#seg cs    #再次将代码段寄存器的值移动到ES寄存器
	cmp 	%cs:sectors+0, %ax    #比较AX和sectors，检查是否已经读取了所有扇区
	jne 	ok3_read    #如果没有，跳转到ok3_read
	mov 	$1, %ax    #设置AX为1
	sub 	head, %ax    #从1减去head的值，检查是否需要更新track
	jne 	ok4_read    #如果不相等，跳转到ok4_read
	incw    track     #如果相等，增加track的值
ok4_read:
	mov	%ax, head    #更新head的值
	xor	%ax, %ax    #清零AX寄存器
ok3_read:
	mov	%ax, sread    #更新sread的值
	shl	$9, %cx    #将CX（扇区数）左移9位，转换为字节偏移量
	add	%cx, %bx    #将偏移量加到BX（段内起始地址）
	jnc	rp_read    #如果没有发生进位，跳转到rp_read继续读取
	mov	%es, %ax     #将ES寄存器的值移动到AX寄存器
	add	$0x1000, %ax    #增加AX的值，移动到下一个64KB段
	mov	%ax, %es    #更新ES寄存器的值
	xor	%bx, %bx    #清零BX寄存器，重置段内起始地址
	jmp	rp_read    #跳转到rp_read继续读取

read_track:
	push	%ax    #保存AX寄存器的值
	push	%bx    #保存BX寄存器的值
	push	%cx    #保存CX寄存器的值
	push	%dx    #保存DX寄存器的值
	mov	track, %dx    #将track的值移动到DX寄存器
	mov	sread, %cx    #将sread的值移动到CX寄存器
	inc	%cx    #增加CX的值，准备读取下一个扇区
	mov	%dl, %ch    #将DX的低8位（即CL）移动到CH
	mov	head, %dx    #将head的值移动到DX寄存器
	mov	%dl, %dh    #将DX的低8位（即DL）移动到DH
	mov	$0, %dl    #清零DL寄存器
	and	$0x0100, %dx    #取DX的第8位，设置为0，其他位清零
	mov	$2, %ah    #设置AH为2，准备读取扇区
	int	$0x13    #调用BIOS中断0x13，执行读取操作
	jc	bad_rt    #如果读取失败，跳转到bad_rt
	pop	%dx    #恢复DX寄存器的值
	pop	%cx    #恢复CX寄存器的值
	pop	%bx    #恢复BX寄存器的值
	pop	%ax    #恢复AX寄存器的值
	ret    #返回到调用read_track的地方
bad_rt:	mov	$0, %ax    #设置AX为0
	mov	$0, %dx    #设置DX为0
	int	$0x13    #再次调用BIOS中断0x13，执行读取操作
	pop	%dx    #恢复DX寄存器的值
	pop	%cx    #恢复CX寄存器的值
	pop	%bx    #恢复BX寄存器的值
	pop	%ax    #恢复AX寄存器的值
	jmp	read_track    #跳转回read_track，尝试重新读取

#/*
# * This procedure turns off the floppy drive motor, so
# * that we enter the kernel in a known state, and
# * don't have to worry about it later.
# */
kill_motor:
	push	%dx
	mov	$0x3f2, %dx
	mov	$0, %al
	outsb
	pop	%dx
	ret

sectors:
	.word 0

msg1:
	.byte 13,10
	.ascii "IceCityOS is booting ..."
	.byte 13,10,13,10

	.org 508
root_dev:
	.word ROOT_DEV
boot_flag:
	.word 0xAA55
	
	.text
	endtext:
	.data
	enddata:
	.bss
	endbss:
